/*
	This file is part of PropExtractor.

	PropExtractor is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	PropExtractor is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with PropExtractor. If not, see <http://www.gnu.org/licenses/>.
*/

#include "PEIncludes.h"

#include "PEAliceSoftZipped.h"

#include "PEFile.h"

namespace pe
{

	int AliceZippedReadHeader(AliceZippedHeader* a_Header, PEStream* a_Stream)
	{
		PEStreamRead(a_Stream, a_Header, sizeof(AliceZippedHeader), 1);

		PE_DEBUG("Zipped header: %s", a_Header->header);

		if (a_Header->header[0] != 'Z' ||
			a_Header->header[1] != 'L' ||
			a_Header->header[2] != 'B')
		{
			return -1;
		}

		return PE_OK;
	}

	int AliceZippedRead(AliceZipped* a_Target, AliceZippedHeader* a_Header, PEStream* a_Stream)
	{
		a_Target->header = a_Header;
		a_Target->datasize = a_Header->datasize;
		a_Target->data = new byte[a_Target->datasize];

		z_stream zlib_stream;
		zlib_stream.zalloc = Z_NULL;
		zlib_stream.zfree = Z_NULL;
		zlib_stream.opaque = Z_NULL;
		zlib_stream.avail_in = 0;
		zlib_stream.next_in = Z_NULL;

		inflateInit(&zlib_stream);

		byte* dst = a_Target->data;
		unsigned int have;
		int ret;
		unsigned char in[CHUNK];
		unsigned char out[CHUNK];
		unsigned int total_compressed = 0;
		unsigned int total_read = 0;

		/*unsigned int pos_curr;
		PEStreamGetCurrentPos(a_Stream, &pos_curr);

		PEStreamSeek(a_Stream, 0, SEEK_END);

		unsigned int pos_end;
		PEStreamGetCurrentPos(a_Stream, &pos_end);

		unsigned int pos_size = pos_end - pos_curr;
		PEStreamSeek(a_Stream, pos_curr, SEEK_SET);*/

		/*unsigned int uncompressed_size = a_Target->header->unknown1;
		byte* src_buffer = new byte[uncompressed_size];
		PEStreamRead(a_Stream, src_buffer, uncompressed_size, 1);

		uncompress(
			a_Target->data,
			(unsigned long*)&total_read,
			src_buffer, 
			uncompressed_size
		);

		int i = 0;*/

		do 
		{
			if (PEStreamRead(a_Stream, in, 1, CHUNK) != PE_OK)
			{
				(void)inflateEnd(&zlib_stream);
				return Z_ERRNO;
			}

			PEStreamGetBytesRead(a_Stream, &zlib_stream.avail_in);

			if (zlib_stream.avail_in == 0) 
			{ 
				break; 
			}
			zlib_stream.next_in = in;

			total_compressed += zlib_stream.avail_in;

			do 
			{
				zlib_stream.avail_out = CHUNK;
				zlib_stream.next_out = out;
				ret = inflate(&zlib_stream, Z_NO_FLUSH);

				switch (ret) 
				{
				case Z_NEED_DICT:
					ret = Z_DATA_ERROR;					

				case Z_DATA_ERROR:
				case Z_MEM_ERROR:
					(void)inflateEnd(&zlib_stream);
					return ret;
				}

				have = CHUNK - zlib_stream.avail_out;
				total_read += have;
				memcpy(dst, out, have);
				dst += have;
			}
			while (zlib_stream.avail_out == 0);
		} 
		while (ret != Z_STREAM_END);

		a_Target->handle = new PEStream;
		PEStreamByteOpen(a_Target->handle, a_Target->data, a_Target->datasize);

		return PE_OK;
	}

}